package org.wgc.study.config;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider;
import org.springframework.batch.item.database.JdbcBatchItemWriter;
import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.wgc.study.entity.Bloginfo;
import org.wgc.study.job.BlogInfoStatementSetter;
import org.wgc.study.job.UserPreStatementSetter;
import org.wgc.study.listner.MyBeanValidator;
import org.wgc.study.listner.MyItemProcessor;
import org.wgc.study.listner.MyJobListener;
import org.wgc.study.listner.MyReadListener;
import org.wgc.study.listner.MyWriteListener;
import org.wgc.study.vo.User;

import javax.sql.DataSource;

/***
 *@title MybatisPlusConfig
 *@description <TODO description class purpose>
 *@author wgc_j
 *@version 1.0.0
 *@create 2025-08-06 15:05
 **/
@Component
public class MybatisPlusConfig {
    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    @Autowired
    private StepBuilderFactory stepBuilderFactory;
    @Autowired
    private DataSource dataSource;
    /**
     * JobRepository定义：Job的注册容器以及和数据库打交道（事务管理等）
     *
     * @param dataSource
     * @param transactionManager
     * @return
     * @throws Exception
     */
    public JobRepository myJobRepository(DataSource dataSource, PlatformTransactionManager transactionManager) throws Exception {
        JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
        jobRepositoryFactoryBean.setDatabaseType("mysql");
        jobRepositoryFactoryBean.setTransactionManager(transactionManager);
        jobRepositoryFactoryBean.setDataSource(dataSource);
        return jobRepositoryFactoryBean.getObject();
    }

    /**
     * jobLauncher定义：job的启动器,绑定相关的jobRepository
     *
     * @param dataSource
     * @param transactionManager
     * @return
     * @throws Exception
     */
    @Bean
    public SimpleJobLauncher myJobLauncher(DataSource dataSource, PlatformTransactionManager transactionManager) throws Exception {
        SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
        // 设置jobRepository
        jobLauncher.setJobRepository(myJobRepository(dataSource, transactionManager));
        return jobLauncher;
    }


    /**
     * 定义job
     *
     * @param jobs
     * @param myStep
     * @return
     */
    @Bean("myJob")
    public Job myJob() {
        return jobBuilderFactory.get("myJob").incrementer(new RunIdIncrementer()).flow(myStep()).end().listener(myJobListener()).build();
    }

    /**
     * 注册job监听器
     *
     * @return
     */
    public MyJobListener myJobListener() {
        return new MyJobListener();
    }


    /**
     * ItemReader定义：读取文件数据+entirty实体类映射
     *
     * @return
     */
    public ItemReader<Bloginfo> reader() {
        // 使用FlatFileItemReader去读cvs文件，一行即一条数据
        FlatFileItemReader<Bloginfo> reader = new FlatFileItemReader<>();
        // 设置文件处在路径
        reader.setResource(new ClassPathResource("bloginfo.csv"));
        // entity与csv数据做映射
        reader.setLineMapper(new DefaultLineMapper<Bloginfo>() {
            {
                setLineTokenizer(new DelimitedLineTokenizer() {
                    {
                        setNames(new String[]{"blogAuthor", "blogUrl", "blogTitle", "blogItem"});
                    }
                });
                setFieldSetMapper(new BeanWrapperFieldSetMapper<Bloginfo>() {
                    {
                        setTargetType(Bloginfo.class);
                    }
                });
            }
        });
        return reader;
    }

    /**
     * 注册ItemProcessor: 处理数据+校验数据
     *
     * @return
     */
    public ItemProcessor<Bloginfo, Bloginfo> processor() {
        MyItemProcessor myItemProcessor = new MyItemProcessor();
        // 设置校验器
        myItemProcessor.setValidator(myBeanValidator());
        return myItemProcessor;
    }

    /**
     * 注册校验器
     *
     * @return
     */
    public MyBeanValidator myBeanValidator() {
        return new MyBeanValidator<Bloginfo>();
    }
    public BlogInfoStatementSetter preStatementSetter(){
        return new BlogInfoStatementSetter();
    }
    /**
     * ItemWriter定义：指定datasource，设置批量插入sql语句，写入数据库
     *
     * @param dataSource
     * @return
     */
    public ItemWriter<Bloginfo> writer(DataSource dataSource) {
        // 使用jdbcBcatchItemWrite写数据到数据库中
    /*    JdbcBatchItemWriter<Bloginfo> writer = new JdbcBatchItemWriter<>();
        // 设置有参数的sql语句
        writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Bloginfo>());
        String sql = "insert into bloginfo " + " (blogAuthor,blogUrl,blogTitle,blogItem) " + " values(:blogAuthor,:blogUrl,:blogTitle,:blogItem)";
        writer.setSql(sql);
        writer.setDataSource(dataSource);
        writer.itemPreparedStatementSetter(preStatementSetter())
                .build();*/
        return new JdbcBatchItemWriterBuilder<Bloginfo>()
                .dataSource(dataSource)
                .sql("insert into bloginfo(blogAuthor, blogUrl, blogTitle,blogItem) values(?,?,?,?)")
                .itemPreparedStatementSetter(preStatementSetter())
                .build();

    }

    /**
     * step定义：
     * 包括
     * ItemReader 读取
     * ItemProcessor  处理
     * ItemWriter 输出
     *

     * @return
     */

    public Step myStep() {
        return stepBuilderFactory.get("myStep").<Bloginfo, Bloginfo>chunk(65000) // Chunk的机制(即每次读取一条数据，再处理一条数据，累积到一定数量后再一次性交给writer进行写入操作)
                .reader(reader()).faultTolerant().retryLimit(3).retry(Exception.class)
                .skip(Exception.class).skipLimit(2)
                .listener(new MyReadListener())
                //.processor(processor())
                .writer(writer(dataSource))
                .faultTolerant().skip(Exception.class)
                .skipLimit(2).listener(new MyWriteListener()).build();
    }
}
